package com.ruoyi.system.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.constant.OrderConstants;
import com.ruoyi.common.enums.BsJingquTypeEnum;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.IdCardUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.system.domain.BsJingqu;
import com.ruoyi.system.domain.BsUserContract;
import com.ruoyi.system.domain.ro.BsJingquSearchPage;
import com.ruoyi.system.domain.ro.index.*;
import com.ruoyi.system.mapper.BsJingquMapper;
import com.ruoyi.system.service.*;
import com.sun.org.apache.bcel.internal.generic.NEW;
import io.github.classgraph.utils.Join;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.compress.utils.Sets;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 *
 */
@Service
@Slf4j
public class BsJingquServiceImpl extends ServiceImpl<BsJingquMapper, BsJingqu>
        implements BsJingquService {

    @Resource
    private BsJingquJingdianService bsJingquJingdianService;
    @Resource
    private BsOrderInfoService bsOrderInfoService;
    @Resource
    private ISysLogininforService sysLogininforService;
    @Resource
    private BsUserContractService contractService;

    @Override
    public boolean saveAndSaveJingDian(BsJingqu bsJingqu) {
        bsJingqu.setDelFlag(Constants.LOGIC_NOT_DELETE);
        boolean result = save(bsJingqu);
        if (result && StringUtils.isNotEmpty(bsJingqu.getJingdianList())) {
            bsJingqu.getJingdianList().forEach(
                    x -> {
                        x.setJingquId(bsJingqu.getJingquId());
                    }
            );
            return bsJingquJingdianService.saveBatch(bsJingqu.getJingdianList());
        }
        return result;
    }

    @Override
    public List<BsJingqu> listWithJingDian() {
        return baseMapper.listWithJingDian();
    }

    @Override
    public boolean updateAndupdateJingDian(BsJingqu bsJingqu) {
        boolean result = updateById(bsJingqu);
        if (result && StringUtils.isNotEmpty(bsJingqu.getJingdianList())) {
            return bsJingquJingdianService.updateBatchById(bsJingqu.getJingdianList());
        }
        return result;
    }

    @Override
    public boolean logicRemove(List<Long> ids) {
        List<BsJingqu> bsJingqus = listByIds(ids);
        bsJingqus.forEach(x -> x.setDelFlag(Constants.LOGIC_DELETE));
        return updateBatchById(bsJingqus);
    }

    @Override
    public BsJingqu getJingQuDetails(Long jingquId) {
        return baseMapper.getJingQuDetails(jingquId);
    }

    @Override
    public Map<String, Object> getSituation(Long id) {
        Map<String, Object> resultMap = new HashMap<>();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        SimpleDateFormat sdfMonth = new SimpleDateFormat("yyyy-MM");
        SimpleDateFormat sdfYear = new SimpleDateFormat("yyyy");
        resultMap.put("todayPeople", bsOrderInfoService.getPeopleCountByDate(OrderConstants.USE_STATUS_WAIT, sdf.format(new Date()), id));
        resultMap.put("monthPeople", bsOrderInfoService.getPeopleCountByMonth(OrderConstants.USE_STATUS_WAIT, sdfMonth.format(new Date()), id));
        resultMap.put("lastdayPeople", bsOrderInfoService.getPeopleCountByDate(OrderConstants.USE_STATUS_WAIT, sdf.format(DateUtils.addDays(new Date(), -1)), id));
        resultMap.put("monthPrice", bsOrderInfoService.getCostByMonth(OrderConstants.USE_STATUS_WAIT, sdfMonth.format(new Date()), id));
        Integer yearPeople = bsOrderInfoService.getPeopleCountByYear(OrderConstants.USE_STATUS_WAIT, sdf.format(new Date()), id);
        Integer lastYearPeople = bsOrderInfoService.getPeopleCountByYear(OrderConstants.USE_STATUS_WAIT, sdf.format(DateUtils.addYears(new Date(), -1)), id);
        yearPeople = yearPeople == null ? 0 : yearPeople;
        if (lastYearPeople == null || lastYearPeople.equals(0)) {
            resultMap.put("peopleYOY", 0);
            resultMap.put("propleYOYstatus", "up");
        } else {
            // 同比比例
            resultMap.put("peopleYOY", BigDecimal.valueOf(Math.abs(yearPeople - lastYearPeople)).divide(BigDecimal.valueOf(lastYearPeople), 2, RoundingMode.HALF_UP).multiply(BigDecimal.valueOf(100)));
            // 同比状态
            resultMap.put("propleYOYstatus", "down");
            if (yearPeople >= lastYearPeople) {
                resultMap.put("propleYOYstatus", "up");
            }
        }
        Integer seasonPeople = bsOrderInfoService.getPeopleCountBySeason(OrderConstants.USE_STATUS_WAIT, sdfYear.format(new Date()), DateUtils.getNowDayQuarter(), id);
        Integer lastSeasonPeople = null;
        if (DateUtils.getLastQuarter() == 0) {
            lastSeasonPeople = bsOrderInfoService.getPeopleCountBySeason(OrderConstants.USE_STATUS_WAIT, sdfYear.format(DateUtils.addYears(new Date(), -1)), 4, id);
        } else {
            lastSeasonPeople = bsOrderInfoService.getPeopleCountBySeason(OrderConstants.USE_STATUS_WAIT, sdfYear.format(new Date()), DateUtils.getLastQuarter(), id);
        }
        seasonPeople = seasonPeople == null ? 0 : seasonPeople;
        if (lastSeasonPeople == null || lastSeasonPeople.equals(0)) {
            resultMap.put("peopleQOQ", 0);
            resultMap.put("propleQOQstatus", "up");
        } else {
            // 环比比例
            resultMap.put("peopleQOQ", BigDecimal.valueOf(Math.abs(seasonPeople - lastSeasonPeople)).divide(BigDecimal.valueOf(lastYearPeople), 2, RoundingMode.HALF_UP).multiply(BigDecimal.valueOf(100)));
            // 环比状态
            resultMap.put("peopleQOQstatus", "down");
            if (seasonPeople >= lastSeasonPeople) {
                resultMap.put("propleQOQstatus", "up");
            }
        }
        BigDecimal yearCost = bsOrderInfoService.getCostByYear(OrderConstants.USE_STATUS_WAIT, sdf.format(new Date()), null);
        BigDecimal lastYearCost = bsOrderInfoService.getCostByYear(OrderConstants.USE_STATUS_WAIT, sdf.format(DateUtils.addYears(new Date(), -1)), null);
        yearCost = yearCost == null ? BigDecimal.ZERO : yearCost;
        if (lastYearCost == null || lastYearCost.doubleValue() == 0) {
            resultMap.put("costYOY", 0);
            resultMap.put("costYOYstatus", "up");
        } else {
            // 同比比例
            resultMap.put("costYOY", yearCost.subtract(lastYearCost).abs().divide(BigDecimal.valueOf(lastYearPeople), 4, RoundingMode.HALF_UP).multiply(BigDecimal.valueOf(100)));
            // 同比状态
            resultMap.put("costYOYstatus", "down");
            if (yearCost.doubleValue() >= lastYearCost.doubleValue()) {
                resultMap.put("costYOYstatus", "up");
            }
        }
        BigDecimal seasonCost = bsOrderInfoService.getCostBySeason(OrderConstants.USE_STATUS_WAIT, sdfYear.format(new Date()), DateUtils.getNowDayQuarter(), id);
        BigDecimal lastSeasonCost = null;
        if (DateUtils.getLastQuarter() == 0) {
            lastSeasonCost = bsOrderInfoService.getCostBySeason(OrderConstants.USE_STATUS_WAIT, sdfYear.format(DateUtils.addYears(new Date(), -1)), 4, id);
        } else {
            lastSeasonCost = bsOrderInfoService.getCostBySeason(OrderConstants.USE_STATUS_WAIT, sdfYear.format(new Date()), DateUtils.getLastQuarter(), id);
        }
        seasonCost = seasonCost == null ? BigDecimal.ZERO : seasonCost;
        if (lastYearCost == null || lastYearCost.doubleValue() == 0) {
            resultMap.put("costQOQ", 0);
            resultMap.put("costQOQstatus", "up");
        } else {
            // 环比比例
            resultMap.put("costQOQ", seasonCost.subtract(lastSeasonCost).abs().divide(BigDecimal.valueOf(lastYearPeople), 4, RoundingMode.HALF_UP).multiply(BigDecimal.valueOf(100)));
            // 环比状态
            resultMap.put("costQOQstatus", "down");
            if (seasonCost.doubleValue() >= lastSeasonCost.doubleValue()) {
                resultMap.put("costQOQstatus", "up");
            }
        }
        return resultMap;
    }

    @Override
    public Map<String, Object> getInfo(Long id) {
        Map<String, Object> resultMap = new HashMap<>();
        resultMap.put("totalAccess", sysLogininforService.count());
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        resultMap.put("todayPeople", bsOrderInfoService.getPeopleCountByDate(OrderConstants.USE_STATUS_WAIT, sdf.format(new Date()), id));
        resultMap.put("totalPeople", bsOrderInfoService.getPeopleCountByDate(OrderConstants.USE_STATUS_WAIT, null, id));
        resultMap.put("lastdayPeople", bsOrderInfoService.getPeopleCountByDate(OrderConstants.USE_STATUS_WAIT, sdf.format(DateUtils.addDays(new Date(), -1)), id));
        return resultMap;
    }

    @Override
    public void runAsyncAppendOrderNum(HashSet<Long> ids) {
        CompletableFuture.runAsync(() -> {
            //冗余热搜频次
            log.info("do 冗余热搜频次");
            if (!ids.isEmpty()) {
                String join = Join.join(",", ids);
                getBaseMapper().appendOrderNum(join);
            }
        });
    }

    @Override
    public void runAsyncAppendOrderNum(String jignquId) {
        try {
            runAsyncAppendOrderNum(Sets.newHashSet(Long.parseLong(jignquId)));
        } catch (Exception e) {
            log.info("BsJingquServiceImpl.runAsyncAppendOrderNum.e:{}", e.getMessage());
        }
    }

    @Override
    public List<Map<String, Object>> toTree(List<BsJingqu> jingquList){
        Map<Integer, List<BsJingqu>> collect = jingquList.stream()
                .collect(Collectors.groupingBy(BsJingqu::getType));
        List<Map<String, Object>> resultList = new ArrayList<>();
        for (BsJingquTypeEnum value : BsJingquTypeEnum.values()) {
            Map<String,Object> map = new HashMap<>();
            map.put("jingquName", value.getName());
            map.put("children",collect.get(value.getType()));
            resultList.add(map);
        }
        return resultList;
    }

    @Override
    public List<BsJingqu> listPage(BsJingquSearchPage param) {
        return baseMapper.listPage(param);
    }

    @Override
    public PeopleNum getPeopleNum() {
        PeopleNum peopleNum = baseMapper.selectPeopleCount();
        return peopleNum;
    }

    @Override
    public List<JingquPeopleNum> getJingquPeople(Integer limit) {
        return baseMapper.getJingquPeopleNum(limit);
    }

    @Override
    public List<CityPeopleNum> getCityPeopleNum() {
        return baseMapper.getContractIdsWithOrderUse();
    }

    @Override
    public List<CityPeopleNum> dealWithCity(List<CityPeopleNum> cityPeopleNum) {
        List<Long> ids = getIds(cityPeopleNum);
        if (ids.isEmpty()){
            return Collections.emptyList();
        }
        Map<String,CityPeopleNum> map = new HashMap<>();
        // 查询出所有的联系人
        Map<Long, String> contractMap = getContractMap(ids);
        // 获取联系人身份证所在的归属地
        ids.forEach(x -> {
            String card = contractMap.get(x);
            if (StringUtils.isNotEmpty(card)){
                String city = IdCardUtils.getProvinceByIdCard(card);
                if (StringUtils.isNotNull(city)){
                    CityPeopleNum peopleNum = map.getOrDefault(city, new CityPeopleNum());
                    peopleNum.setCityName(city);
                    peopleNum.setNum(Objects.isNull(peopleNum.getNum()) ? BigDecimal.ONE : peopleNum.getNum().add(BigDecimal.ONE));
                    map.put(city,peopleNum);
                }
            }
        });
        return new ArrayList<>(map.values());
    }

    @Override
    public List<AgePeopleNum> dealWithAge(List<CityPeopleNum> cityPeopleNum) {
        List<Long> ids = getIds(cityPeopleNum);
        if (ids.isEmpty()){
            return Collections.emptyList();
        }
        // 查询出所有的联系人
        Map<Long, String> contractMap = getContractMap(ids);
        Map<Integer, AgePeopleNum> ageMap = getAgeMap();
        ids.forEach(x -> {
            String card = contractMap.get(x);
            if (StringUtils.isNotEmpty(card)){
                int age = IdCardUtils.getAge(card);
                Integer index = null;
                if (age > 0 && age < 18){
                    index = 0;
                } else if (age >= 18 && age < 25){
                    index = 1;
                } else if (age >= 25 && age < 45){
                    index = 2;
                } else if (age >= 45 && age < 60){
                    index = 3;
                } else if (age >= 60 ){
                    index = 4;
                }
                if (StringUtils.isNotNull(index)){
                    AgePeopleNum ageNum = ageMap.get(index);
                    ageNum.setNum(ageNum.getNum().add(BigDecimal.ONE));
                    ageMap.put(index,ageNum);
                }
            }
        });
        ArrayList<AgePeopleNum> list = new ArrayList<>(ageMap.values());
        double sum = list.stream().mapToDouble(x -> x.getNum().doubleValue()).sum();
        list.forEach(x -> {
            if (sum == 0 || x.getNum().doubleValue() == 0){
                x.setPercent(BigDecimal.ZERO.doubleValue() + "%");
            } else {
                x.setPercent(x.getNum().divide(BigDecimal.valueOf(sum),4, RoundingMode.HALF_UP).multiply(BigDecimal.valueOf(100)).doubleValue() + "%");
            }
        });
        return list;
    }

    @Override
    public List<TimePeopleNum> getTimePeopleNum() {
        Map<String, Integer> today = baseMapper.getOrderNumForTime(false)
                .stream().collect(Collectors.toMap(
                        TimePeopleNum::getTime,
                        TimePeopleNum::getNum,
                        (a, b) -> b
                ));
        Map<String, Integer> yesterday = baseMapper.getOrderNumForTime(true)
                .stream().collect(Collectors.toMap(
                        TimePeopleNum::getTime,
                        TimePeopleNum::getNum,
                        (a, b) -> b
                ));
        List<String> dates = DateUtils.generateTimeSlots();
        List<TimePeopleNum> resultList = new ArrayList<>();
        for (String date : dates) {
            TimePeopleNum timePeopleNum = new TimePeopleNum();
            timePeopleNum.setTime(date);
            Integer todayNum = today.get(date);
            Integer yesterdayNum = yesterday.get(date);
            timePeopleNum.setTodayNum(StringUtils.isNull(todayNum) ? 0 : todayNum);
            timePeopleNum.setYesterdayNum(StringUtils.isNull(yesterdayNum) ? 0 : yesterdayNum);
            timePeopleNum.setAverageNum((timePeopleNum.getTodayNum() + timePeopleNum.getYesterdayNum()) / 2.0);
            resultList.add(timePeopleNum);
        }
        return resultList;
    }

    @Override
    public int getPeopleNumForDay(Date date) {
        return baseMapper.getPeopleNumForDay(date);
    }


    public Map<Integer,AgePeopleNum> getAgeMap(){
        Map<Integer,AgePeopleNum> map = new HashMap<>(8);
        map.put(0, new AgePeopleNum(0, "18岁以下", BigDecimal.ZERO, "0%",null));
        map.put(1, new AgePeopleNum(1, "18-25岁", BigDecimal.ZERO, "0%",null));
        map.put(2, new AgePeopleNum(2, "25-45岁", BigDecimal.ZERO, "0%",null));
        map.put(3, new AgePeopleNum(3, "45-60岁", BigDecimal.ZERO, "0%",null));
        map.put(4, new AgePeopleNum(4, "60岁以上", BigDecimal.ZERO, "0%",null));
        return map;
    }

    public List<Long> getIds(List<CityPeopleNum> cityPeopleNum){
        List<Long> ids = new ArrayList<>();
        // 对已入园订单中的所有游客ID进行拆解
        cityPeopleNum.forEach(x -> {
            String[] split = x.getContractIds().split(",");
            for (String s : split) {
                ids.add(Long.parseLong(s));
            }
        });
        return ids;
    }

    public Map<Long, String> getContractMap(List<Long> ids){
        Map<Long, String> contractMap = contractService.listByIds(ids)
                .stream().collect(Collectors.toMap(
                        BsUserContract::getContractId,
                        BsUserContract::getContractCard,
                        (a, b) -> b
                ));
        return contractMap;
    }

}




